package producersConsumersTests.results;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
import java.util.Map;

import producersConsumersTests.InserterThroughput.InsertionThroughputTest;
import producersConsumersTests.RemoverThroughput.RemoverThroughputTest;
import producersConsumersTests.WaitingTime.TimedTask;
import producersConsumersTests.WaitingTime.WaitingTimeTest;
import producersConsumersTests.bypassersCount.BypassCountTest;
import producersConsumersTests.bypassersCount.BypassCountTestV2;
import producersConsumersTests.meanInsertionTime.MeanInsertionTimeTest;
import producersConsumersTests.meanRemovalTime.MeanRemovalTimeTest;
import producersConsumersTests.utils.Pair;
import producersConsumersTests.utils.TesterVisitor;

public class ResultPrinter implements TesterVisitor {
	private Writer out;

	private static ResultPrinter printer;

	private static final int MAX_ROWS = 1048000;

	private ResultPrinter(String filename, int testNum, int insertersNum,
			int removersNum, int peakLength, int timeBetweenPeaks,
			int timeToRun, String containerName, int extraInt) {
		try {
			out = new BufferedWriter(new FileWriter(filename));
			startWrite(testNum, insertersNum, removersNum, timeToRun, extraInt,
					containerName);
			out.write("runType, B,\n" + "peakLength, " + peakLength + "\n"
					+ "timeBetweenPeaks, " + timeBetweenPeaks + "\n");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private ResultPrinter(String filename, int testNum, int insertersNum,
			int removersNum, double lambda, int timeToRun,
			String containerName, int extraInt) {
		try {
			out = new BufferedWriter(new FileWriter(filename));
			startWrite(testNum, insertersNum, removersNum, timeToRun, extraInt,
					containerName);
			out.write("runType, P,\n" + "lambda, " + lambda + "\n");
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private void startWrite(int testNum, int insertersNum, int removersNum,
			int timeToRun, int extraInt, String container) throws IOException {
		out.write("test type," + testNum + "\ninsertersNum," + insertersNum
				+ "\nremoversNum," + removersNum + "\n" + "timeToRun,"
				+ timeToRun + "\nextra," + extraInt + "\ncontainer,"
				+ container + "\n\n");
	}

	private void finishWrite() throws IOException {
		out.close();
	}

	public static void init(String filename, int testNum, int insertersNum,
			int removersNum, double lambda, int timeToRun, String container,
			int extraInt) {
		if (printer != null)
			return;
		printer = new ResultPrinter(filename, testNum, insertersNum,
				removersNum, lambda, timeToRun, container, extraInt);
	}

	public static void init(String filename, int testNum, int insertersNum,
			int removersNum, int peakLength, int timeBetweenPeaks,
			int timeToRun, String container, int extraInt) {
		if (printer != null)
			return;
		printer = new ResultPrinter(filename, testNum, insertersNum,
				removersNum, peakLength, timeBetweenPeaks, timeToRun,
				container, extraInt);
	}

	public static ResultPrinter get() {
		return printer;
	}

	@Override
	public void visit(InsertionThroughputTest test) {
		try {
			// No space problem here.
			for (Map.Entry<Integer, List<Long>> e : test.getResults()
					.entrySet()) {
				out.write("second," + e.getKey() + "\n");
				for (Long l : e.getValue())
					out.write("insertions, " + l + "\n");
			}
			out.write("\nAVG," + test.getAverage() + "\n");
			finishWrite();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
	}

	@Override
	public void visit(RemoverThroughputTest test) {
		try {
			// No space problem here.
			for (Map.Entry<Integer, List<Long>> e : test.getResults()
					.entrySet()) {
				out.write("second," + e.getKey() + "\n");
				for (Long l : e.getValue())
					out.write("removals, " + l + "\n");
			}
			out.write("\nAVG," + test.getAverage() + "\n");
			finishWrite();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
	}

	@Override
	public void visit(MeanInsertionTimeTest test) {
		try {
			int i = 0, actsPerRow = 1, size = test.getResults().size();
			while (size > MAX_ROWS) {
				actsPerRow *= 2;
				actsPerRow /= 2;
			}
			out.write("actsPerRow," + actsPerRow + "\n");
			long sum = 0, max = 0;
			for (Long e : test.getResults()) {
				if (e > max)
					max = e;
				sum += e;
				if (++i >= actsPerRow) {
					out.write("ms, " + sum + "\n");
					sum = i = 0;
				}
			}
			out.write("\nMAX," + max + "\n");
			out.write("AVG," + test.getAverage() + "\n");
			finishWrite();

		} catch (IOException e1) {
			e1.printStackTrace();
		}
	}

	@Override
	public void visit(MeanRemovalTimeTest test) {
		try {
			int i = 0, actsPerRow = 1, size = test.getResults().size();
			while (size > MAX_ROWS) {
				actsPerRow *= 2;
				size /= 2;
			}
			out.write("actsPerRow," + actsPerRow + "\n");
			double sumTimes = 0, max = 0;
			long sumRemoves = 0;
			for (Pair<Double, Integer> e : test.getResults()) {
				if (e.second > 0) {
					double time = e.first / e.second;
					if (time > max)
						max = time;
				}
				sumTimes += e.first;
				sumRemoves += e.second;
				if (++i >= actsPerRow) {
					out.write("period," + sumTimes + "\n");
					out.write("removals," + sumRemoves + "\n");
					sumTimes = sumRemoves = i = 0;
				}
			}
			out.write("\nmax," + max + "\n");
			out.write("avg," + test.getAverage() + "\n");
			finishWrite();

		} catch (IOException e1) {
			e1.printStackTrace();
		}
	}

	@Override
	public void visit(WaitingTimeTest test) {
		try {
			out.write("inserterWhen," + test.getInserterWhen() + "\n"
					+ "removerWhen," + test.getRemoverWhen() + "\n\n");
			int i = 0, actsPerRow = 1, size = test.getResults().size();
			while (size > MAX_ROWS) {
				actsPerRow *= 2;
				size /= 2;
			}
			out.write("actsPerRow," + actsPerRow + "\n");
			long sum = 0, max = 0;
			for (TimedTask t : test.getResults()) {
				long diff = t.getExitTime() - t.getEnterTime();
				if (diff <= 0)
					sum += diff;
				if (diff > max)
					max = diff;
				if (++i >= actsPerRow) {
					out.write("time," + sum + "\n");
					sum = i = 0;
				}
			}
			out.write("max," + max + "\n");
			out.write("AVG," + test.getAverage() + "\n");
			finishWrite();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
	}

	@Override
	public void visit(BypassCountTest test) {
		try {
			int j = 0, actsPerRow = 1, size = test.getResults().size();
			while (size > MAX_ROWS) {
				actsPerRow *= 2;
				size /= 2;
			}
			out.write("actsPerRow," + actsPerRow + "\n");
			long max = 0, sum = 0;
			for (Long l : test.getEventSupervisor().getMeasures()) {
				sum += l;
				if (l > max)
					max = l;
				if (++j >= actsPerRow) {
					out.write("bypassers," + sum + "\n");
					sum = j = 0;
				}
			}
			out.write("max," + max + "\n");
			out.write("AVG," + test.getAverage() + "\n");
			finishWrite();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
	}

	@Override
	public void visit(BypassCountTestV2 test) {
		try {
			double totalAvg = 0, totalMax = 0;
			out.write("treeHeight," + BypassCountTestV2.treeHeight+"\n\n");
			for (EventSupervisor s : test.getSupervisors()) {
				totalAvg += s.getAverageBypassers();
				totalMax = totalMax < s.getMaxBypassers() ? s.getMaxBypassers()
						: totalMax;
				out.write("avg," + s.getAverageBypassers() + ",max,"
						+ s.getMaxBypassers() + "\n");
			}
			totalAvg /= test.getSupervisors().size();
			out.write("totalAvg," + totalAvg + "\ntotalMax," + totalMax + "\n");
			finishWrite();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
	}
}
